package com.xuecheng.publish.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xuecheng.api.content.model.dto.TeachplanDTO;
import com.xuecheng.common.enums.content.CourseModeEnum;
import com.xuecheng.common.exception.ExceptionCast;
import com.xuecheng.common.util.JsonUtil;
import com.xuecheng.publish.common.constant.CoursePubTemplateKey;
import com.xuecheng.publish.common.constant.CoursePublishErrorCode;
import com.xuecheng.publish.common.util.QiniuUtils;
import com.xuecheng.publish.entity.CourseMarket;
import com.xuecheng.publish.entity.CoursePub;
import com.xuecheng.publish.entity.CoursePubMsg;
import com.xuecheng.publish.mapper.CoursePubMapper;
import com.xuecheng.publish.service.CoursePubService;
import freemarker.template.Configuration;
import freemarker.template.Template;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.ui.freemarker.FreeMarkerTemplateUtils;

import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;

/**
 * <p>
 * 课程发布 服务实现类
 * </p>
 *
 * @author itcast
 */
@Slf4j
@Service
public class CoursePubServiceImpl extends ServiceImpl<CoursePubMapper, CoursePub> implements CoursePubService {


    @Autowired
    private Configuration configuration;

    /*

    #生成静态化页面发布位置
    course.publish.position = pages/
    #七牛云的存储消息
    file.qiniu.accessKey = pFcOu02W6CdSqjY6qmRH1jz--pwfQEVyuM4RkBGC
    file.qiniu.secretKey = z4CqmQb9LYD3AiuViX3ADk3r3-iOAm9yQePGP6Hh
    file.qiniu.bucket = xc134-staitc-pages
        * */

    @Value("${file.qiniu.accessKey}")
    private String accessKey;

    @Value("${file.qiniu.secretKey}")
    private String secretKey;

    @Value("${file.qiniu.bucket}")
    private String bucket;

    @Value("${course.publish.position}")
    private String position;


    /*
    * 业务分析：
    *   1.判断消息的幂等性
    *       PS：在MQ的消费方如果有消息重试，必须要考虑消息的幂等性
    *       使用CoursePub 的 isPub来标识消息的幂等性
    *           0:已经发布页面
    *           1:没有发布页面
    *   2.生成课程页面
    *       数据模型：coursePubId
    *       页面模板：Configuration
    *   3.将生成后的页面信息上传到cdn服务上
    *
    *   4.修改课程发布的幂等值：isPub=0
    * */
    @Transactional
    public void publishPage(CoursePubMsg coursePubMsg) {
        // 测试消息重试
        // int i = 1 / 0;

        //1.判断消息的幂等性
        //     PS：在MQ的消费方如果有消息重试，必须要考虑消息的幂等性
        //     使用CoursePub 的 isPub来标识消息的幂等性
        //         0:已经发布页面
        //         1:没有发布页面
        LambdaQueryWrapper<CoursePub> pubQueryWrapper = new LambdaQueryWrapper<>();
        pubQueryWrapper.eq(CoursePub::getId, coursePubMsg.getPubId());
        pubQueryWrapper.eq(CoursePub::getIsPub, CoursePub.PUBLISHED);

        int count = this.count(pubQueryWrapper);

        // 如果课程页面已经发布，记录消息，但不要抛出异常，抛出异常后，spring-retry会进行重试
        if (count > 0) {
            log.info("课程发布页面已经操作完，无需操作：coursePubId--{}",coursePubMsg.getPubId());
            return;
        }


        // 2.生成课程页面
        //     数据模型：coursePubId
        //     页面模板：Configuration
        CoursePub coursePub = null;
        String htmlString = null;
        try {
            coursePub = this.getById(coursePubMsg.getPubId());
            Map<String, Object> dataMap = generateDataMap(coursePub);

            Template template = configuration.getTemplate("learing_article.ftl");

            htmlString = FreeMarkerTemplateUtils.processTemplateIntoString(template, dataMap);
        } catch (Exception e) {
            log.error(CoursePublishErrorCode.E_120209.getDesc()+" coursePubId:{}",coursePub.getId());
            ExceptionCast.cast(CoursePublishErrorCode.E_120209);
        }


        // 4.修改课程发布的幂等值：isPub=0
        LambdaUpdateWrapper<CoursePub> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.set(CoursePub::getIsPub, CoursePub.PUBLISHED);
        updateWrapper.set(CoursePub::getChangeDate, LocalDateTime.now());
        updateWrapper.eq(CoursePub::getId, coursePubMsg.getPubId());

        boolean result = this.update(updateWrapper);

        if (!result) {

            log.error("课程发布修改发布的状态失败: coursePubId --{}",coursePub.getId());

            // 程序抛出异常，让spring-retry 进行消息的重试
            ExceptionCast.cast(CoursePublishErrorCode.E_120214);
        }

        // 3.将生成后的页面信息上传到cdn服务上
        /*
        * htmlKey:文件标识
        *       要体现出上传的文件目录
        *       文件名称的唯一
        *       和coursePub数据能够对应上
        *
        *      形式：pages/coursePubId.html
        * */
        try {
            String htmlKey = position+coursePubMsg.getPubId()+".html";
            QiniuUtils.upload2Qiniu(accessKey,secretKey,bucket,htmlString,htmlKey);
        } catch (Exception e) {
            log.error(CoursePublishErrorCode.E_120210.getDesc()+" coursePubId:{}",coursePub.getId());
            ExceptionCast.cast(CoursePublishErrorCode.E_120210);
        }


    }


    private Map<String, Object> generateDataMap(CoursePub coursePub) {

        // 1.构建数据模型对象
        HashMap<String, Object> dataMap = new HashMap<>();

        // 2.构建coursePub
        dataMap.put(CoursePubTemplateKey.COURSEPUB, coursePub);

        // 3.构建courseMarket
        String marketJsonString = coursePub.getMarket();
        CourseMarket courseMarket = JsonUtil.jsonToObject(marketJsonString, CourseMarket.class);

        dataMap.put(CoursePubTemplateKey.COURSEMARKET,courseMarket );

        // 4.构建课程计划
        String teachplanJsonString = coursePub.getTeachplan();
        TeachplanDTO teachplanDTO = JsonUtil.jsonToObject(teachplanJsonString, TeachplanDTO.class);
        dataMap.put(CoursePubTemplateKey.TEACHPLANNODE,teachplanDTO );

        // 5.构建课程模式
        CourseModeEnum[] values = CourseModeEnum.values();
        dataMap.put(CoursePubTemplateKey.COURSETEACHMODEENUMS,values );

        return dataMap;
    }
}
